我們在 Memory leak 篇介紹了如何使用 Profiler 來找出 Memory leak。接著將繼續介紹使用 Profiler 來分析記憶體。Profiler 所統計的記憶體使用是指單獨 App 的記憶體使用,不包含系統資源或其他 App。
要開啟 Profiler ,請在工具列點選 View → Tool Windows → Profiler。開啟會看到如下畫面,Profiler 可以用來分析 CPU、Memory 及 ENERGY,點選藍色的記憶體區塊進入記憶體詳細資訊。
進入到記憶體區塊,可以看到 Profiler 將記憶體分為更多的部分來顯示使用量。你可以使用滑鼠在不同的時間軸移動,即會顯示不同時間點的記憶體配置狀況。
MEMORY 的種類:
Java:Java 或 Kotlin 的程式碼所用的記憶體。
Native:C 或 C++ 的程式碼所用的記憶體。
Graphics:圖片緩衝區 Queue 在螢幕上顯示時所用的記憶體。
Stack:Native、Java 的 Stack 使用的記憶體,通常跟執行緒數量有關。
Code:資源檔,如dex程式碼、so 檔、字型的記憶體。
Others:其他分類。
圖表最上方的粉紅色長條區塊時間軸,以圓點來表示使用者與 UI 的互動。例如 Activity 進入 Stopped、Activity 進入 Resumed 或者畫面的點擊,皆會記錄在上方。
在之前的 Out of memory 篇,我們介紹了使用縮圖來載入圖片資料以減入記憶體的使用。這裡就用同樣的範例來測試一下載入一張未縮圖的情況,使用 Profile 能不能看出問題。
binding.loadImage.setOnClickListener {
//未使用縮圖,直接將原圖載入至Imageview
BitmapFactory.decodeResource(resources, R.mipmap.scenery)
}
將 App 執行後開啟 Profiler 後,進入到 Memory 區塊。
選擇 Capture heap dump 後,點選 Record 就可以建立一份記憶體快照。
完成後即可看到如下圖。預設會依照 Retained Size 由大到小排序,所以你只需要看最前面幾筆是不是有異常的部分。下圖很明顯可以看出 Bitmap 是有問題的。
我們再把載入圖片的範例,改為使用縮圖的方式。
binding.loadImage.setOnClickListener {
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
//在圖片載入到記憶體前,先取得圖片的寬高
BitmapFactory.decodeResource(resources, R.mipmap.scenery, options)
val imageHeight: Int = options.outHeight
val imageWidth: Int = options.outWidth
val imageType: String = options.outMimeType
//在ImageView呈現的寬
val smallImageWidth = 200
//在ImageView呈現的高
val smallImageHeight = 200
//記算Size,縮小幾倍
options.inSampleSize = calculateInSampleSize(options, smallImageWidth, smallImageHeight)
options.inJustDecodeBounds = false
binding.imageView.setImageBitmap(
BitmapFactory.decodeResource(resources, R.mipmap.scenery, options)
BitmapFactory.decodeResource(resources, R.mipmap.scenery)
)
}
再次執行 Profiler → Capture heap dump。可以看到沒有特別高的記憶體使用了。
最後,記憶體效能篇就到這篇告一個段落。在效能優化的處理,記憶體管理是非常重要的一環。記憶體在行動裝置上是非常寶貴的資源,應該儘可能的減少浪費。例如資源無法被釋放可能造成 Memory leak,不當的使用記憶體可能造成 Out of memory。另外搭配使用工具 Profiler、LeakCanary 檢測記憶體可以有效的找出效能問題。
參考:
https://developer.android.com/studio/profile/memory-profiler